package com.lime49.lockcrypt;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import org.json.*;

public class Account extends AccountTreeNode implements Transferable, Serializable { //Comparable
    public String type, icon;
    public AccType info;
    public static DataFlavor META_FLAVOR = new DataFlavor(Account.class, "Account");
    private static DataFlavor[] flavors = { META_FLAVOR };
    
    /** Constructs a dummy account to show on the add form when adding a new account
     */
    public Account() {
        super("", "lockcrypt");
        this.type = "Blank";
        this.icon = "default";
    }
    
    /** Constructs a dummy account to show on the add form when adding a new account
     * @param type The type of the account to show
     */
    public Account(AccType type) {
        super("", "lockcrypt");
        this.type = type.getName();
        this.info = type;
        this.icon = "default";
    }
    
    /**
     * Creates an account with the specified parameters
     * @param name The account name
     * @param type The name of the account type
     * @param icon The account icon
     * @param info An AccType object containing the account's fields
     * @param isChanged Whether the account has been changed since the database was loaded
     * @param group The name of the parent group
     * @param created The creation date
     * @param modified The last modified date
     */
    public Account(String name, String type, String icon, AccType info, boolean isChanged, String group, long created, long modified) {
        super(name, group, isChanged, created, modified);
        this.type = type;
        this.info = info;
        this.icon = icon;
    }
    
    /**
     * Returns whether or not to show the notes field for this account
     * @return True if the notes field should be shown, otherwise false
     */
    public boolean getShowNotes() {
        return info.getShowNotes();
    }
    
    /**
     * Returns the notes for this account
     * @return The notes for this account
     */
    public String getNotes() {
        return info.getNotes();
    }
    
    /**
     * Returns the name of the icon for this account
     * @return The icon for this account
     */
    public String getIcon() {
        return icon;
    }
    
    /**
     * Returns the name of the type of this account
     * @return The name of the type of this account
     */
    public String getType() {
        return type;
    }
    
    /**
     * Sets the stype of this account
     * @param type The new type for this account
     */
    public void setType(AccType type) {
        this.type = type.getName();
        this.info = type;
        super.setIsChanged(true);
    }
    
    /**
     * Returns the type object for this account
     * @return This account's AccTupe object containing it's fields
     */
    public AccType getTypeObject() {
        return info;
    }
    
    /**
     * Returns the name of the group of this account
     * @return The group of this account
     */
    public String getGroup() {
        return super.getParentName();
    }
    
    /**
     * Checks whether this account has a field with the specified name
     * @param name The name of the field to check for
     * @return True if the field exists, otherwise false
     */
    public boolean fieldExists(String name) {
        return info.fieldExists(name);
    }
    
    /**
     * Sets the group for this account
     * @param newGroup The new group for this account
     */
    public void setGroup(String newGroup) {
        super.setParentName(newGroup);
        super.setIsChanged(true);
    }
    
    /**
     * Removes the specified field from this account
     * @param fieldname The name of the field to remove
     */
    public void removeField(String fieldname) {
        info.removeField(fieldname);
    }
    
    /**
     * Sets the obfuscated flag on the specified field
     * @param fieldname The name of the field to obfuscate
     */
    public void obfuscateField(String fieldname) {
        if(info.fieldmap.containsKey(fieldname)) {
            /* Field is not obfuscated or marked as a phone number, obfuscate it */
            info.replaceField(fieldname, "~}*"+fieldname);
        } else if(info.fieldmap.containsKey("~}#"+fieldname)) {
            /* Field exists but is marked as phone number */
            info.replaceField("~}#"+fieldname, "~}*"+fieldname);
        } else if(info.fieldmap.containsKey("~}u"+fieldname)) {
            /* Field exists but is marked as phone number */
            info.replaceField("~}u"+fieldname, "~}*"+fieldname);
        } else {
            /* Plain field not found, so obfuscated version must exist */
            info.replaceField("~}*"+fieldname, fieldname);
        }
    }
    
    /**
     * Sets the phone number flag on the specified field
     * @param fieldname The name of the field to mark as a number
     */
    public void togglePhoneNumber(String fieldname) {
        if(info.fieldmap.containsKey(fieldname)) {
            /* Field is not obfuscated or marked as a phone number, mark it as a phone number */
            info.replaceField(fieldname, "~}#"+fieldname);
        } else if(info.fieldmap.containsKey("~}*"+fieldname)) {
            /* Field exists but is marked as obfuscated */
            info.replaceField("~}*"+fieldname, "~}#"+fieldname);
        } else if(info.fieldmap.containsKey("~}u"+fieldname)) {
            /* Field exists but is marked as phone number */
            info.replaceField("~}u"+fieldname, "~}#"+fieldname);
        } else {
            /* Plain field not found, so phone number version must exist */
            info.replaceField("~}#"+fieldname, fieldname);
        }
    }

    /**
     * Sets the URL flag on the specified field
     * @param fieldname The name of the field to mark as a URL
     */
    public void toggleUrlField(String fieldname) {
        if(info.fieldmap.containsKey(fieldname)) {
            /* Field is not obfuscated, marked as a phone number or a URL, mark it as a URL */
            info.replaceField(fieldname, "~}u"+fieldname);
        } else if(info.fieldmap.containsKey("~}*"+fieldname)) {
            /* Field exists but is marked as obfuscated */
            info.replaceField("~}*"+fieldname, "~}u"+fieldname);
        } else if(info.fieldmap.containsKey("~}#"+fieldname)) {
            /* Field exists but is marked as phone number */
            info.replaceField("~}#"+fieldname, "~}u"+fieldname);
        } else {
            /* Plain field not found, so URL version must exist */
            info.replaceField("~}u"+fieldname, fieldname);
        }
    }
    
    /**
     * Returns the value of the specified field
     * @param name The name of the field
     * @return The value associated with that field
     */
    public String getField(String name) {
        if(info.fieldExists(name)) {
            return info.getField(name);
        } else {
            return "";
        }
    }

    public LinkedHashMap<String, String> getFields() {
        return info.getFieldMap();
    }

    /**
     * Sets the contents of a field if it already exists, or adds it if it doesn;t
     * @param name The name of the field
     * @param value The value for the field
     */
    public void setField(String name, String value) {
        info.getFieldMap().put(name, value);
    }
    
    /**
     * Gets the ID of this account't type
     * @return The ID of this account't type
     */
    public String getTypeID() {
        return info.getID();
    }
    
    /**
     * Gets output for writing to a file
     * @param textfile True if writing to a text file, otherwise false
     * @return Output formatted for either a text file or other means of storage
     */
    public String getOutput(boolean textfile) {
        try {
            if(textfile) {
                JSONObject jso = new JSONObject();
                jso.put("Name", getName());
                jso.put("Type", type);
                jso.put("TypeID", info.getID());
                jso.put("Icon", icon);
                jso.put("Parent", getParentName());
                jso.put("Created", getCreated());
                jso.put("Modified", getModified());
                jso.put("ShowNotes", getShowNotes());
                jso.put("Notes", getNotes().replace("\n", "_N_"));
                JSONArray fields = new JSONArray();
                JSONObject currentField;
                LinkedHashMap<String,String> fieldMap = info.getFieldMap();
                Iterator iter = fieldMap.entrySet().iterator();
                while(iter.hasNext()) {
                    Map.Entry entry = (Map.Entry)iter.next();
                    currentField = new JSONObject();
                    currentField.put("Name", entry.getKey().toString());
                    currentField.put("Value", entry.getValue());
                    fields.put(currentField);
                }
                jso.put("Fields", fields);
                return "acc"+jso.toString();
            } else {
                JSONArray fields = new JSONArray();
                JSONObject currentField;
                LinkedHashMap<String,String> fieldMap = info.getFieldMap();
                Iterator iter = fieldMap.entrySet().iterator();
                while(iter.hasNext()) {
                    Map.Entry entry = (Map.Entry)iter.next();
                    currentField = new JSONObject();
                    currentField.put("Name", entry.getKey().toString());
                    currentField.put("Value", entry.getValue());
                    fields.put(currentField);
                }
                return fields.toString();
            }
        } catch(Exception ignore) {}
        return "";
    }
    
    /**
     * Used for searching, returns a condensed summary of the data in this account
     * @return A texttual representation of the the data contained within this account
     */
    public String getSearchString() {
        return super.getName()+type+info.getSearchString();
    }
    
    @Override
    public String toString() {
        return super.getName();
    }
    
    // ---------- Transferable ----------
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.equals(META_FLAVOR);
    }
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if(flavor.equals(META_FLAVOR)) {
            return this;
        } else {
            throw new UnsupportedFlavorException(flavor);
        }
    }
    public DataFlavor[] getTransferDataFlavors() {
        return flavors;
    }
    
    // --------- Serializable --------------
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
      out.defaultWriteObject();
    }
    private void readObject(java.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException {
      in.defaultReadObject();
    }
   
    // ---------- Comparable ----------
    /*public int compareTo(Object otherObj) {
        Account otherAccount = (Account)otherObj;
        return name.toLowerCase().compareTo(otherAccount.getName().toLowerCase());
    } */
}
